const OP = Object.prototype;
const WATCH_FLAG = Symbol("JsonMonitor Flag");
const types = {
  obj: '[object Object]',
  array: '[object Array]',
  number: '[object Number]',
  string: '[object String]'
}
const filterMethodNames = new Set(['addCallback','removeCallback','changedCallback']);
const WATCH_MAP = new Map();
const arrayProto = Array.prototype;
const arrayMethods = Object.create(arrayProto);
const newArrProto = [];
[
  'push',
  'pop',
  'shift',
  'unshift',
  'splice',
  'sort',
  'reverse'
].forEach(method => {
  let original = arrayMethods[method];
  newArrProto[method] = function mutator() {
    let ret = null;
    if (this.watch) {
      switch (method) {
        // 删除指定范围的元素
        case 'splice':
          if (this.watch.removeCallback) {
            let index = arguments[0];
            let count = arguments[1];
            for(let i=0;i<count;i++){
              this.watch.proxyJson.removeCallback(this, index+i, this[index+i]);
            }
          }
          ret = original.apply(this, arguments);
          break;
        // 删除最后一个元素
        case 'pop':
          if (this.length>0 && this.watch.proxyJson.removeCallback) {
            this.watch.proxyJson.removeCallback(this, this.length-1, this[this.length-1]);
          }
          ret = original.apply(this, arguments);
          break;
        // 添加元素
        case 'push':
          ret = original.apply(this, arguments);
          if (this.watch.proxyJson.addCallback) {
            let value = arguments[0];
            this.watch.proxyJson.addCallback(this, this.length - 1, value);
          }
          break;
        default:
          ret = original.apply(this, arguments);
      }
    }

    // 调用对应的原生方法并返回结果（新数组长度）
    return ret;
  }
})

export default class JsonMonitor {
  constructor(obj, addCallback, removeCallback, changedCallback) {
    if (OP.toString.call(obj) === types.array) {
      throw new Error('The following node of JSON cannot be an array!');
    }
    this.proxyJson = this.createProxy(obj);
    this.proxyJson.addCallback = addCallback;
    this.proxyJson.removeCallback = removeCallback;
    this.proxyJson.changedCallback = changedCallback;
    WATCH_MAP.set(this.proxyJson, this);
    console.log('Welcome to the world of JsonMonitor o^-^o');
    return this.proxyJson;
  }
  /**
   * 创建代理
   * @param {*} obj 
   * @returns 
   */
  createProxy(obj) {
    if (!obj || obj === null) {
      return obj;
    } else if (OP.toString.call(obj) === types.obj) {
      return this.createObjectProxy(obj);
    } else if (OP.toString.call(obj) === types.array) {
      return this.createArrayProxy(obj);
    } else {
      return obj;
    }
  }
  /**
   * 创建对象代理
   * @param {*} obj 
   * @returns 
   */
  createObjectProxy(obj) {
    let root = this;
    // 如果对象已经打过标记，则不设置代理
    if (obj[WATCH_FLAG]) {
      return obj[WATCH_FLAG];
    }
    // 设置对象代理
    const proxy = new Proxy(obj, {
      set(target, property, value, receiver) {
        if (!root.proxyJson || root.isFilterMethodName(property)) {
          return Reflect.set(...arguments);
        }
        let oldVal = target[property];
        let isSuccess = false;
        // 如果设置的属性是动态新添加的，则设置代理并赋值
        if (!target.hasOwnProperty(property) && receiver === proxy) {
          isSuccess = Reflect.set(...arguments);
          target[property] = root.createProxy(target[property])
          try {
            if (root.proxyJson.addCallback != null) {
              root.proxyJson.addCallback(target, property, value);
            }
          } catch (err) {
            console.error(err);
          }
        } else {  // 如果属性是更改的，则赋值
          isSuccess = Reflect.set(...arguments);
          // 如果将对象的值设置成了对象或数组，则重新设置代理
          target[property] = root.createProxy(target[property])
          try {
            if (root.proxyJson.changedCallback != null && property !== WATCH_FLAG) {
              root.proxyJson.changedCallback(target, property, oldVal, value);
            }
          } catch (err) {
            console.error(err);
          }
        }
        return isSuccess;
      }
    });
    // 给对象打上标记属性
    Reflect.set(obj, WATCH_FLAG, proxy);
    Reflect.set(proxy, WATCH_FLAG, proxy);
    // 遍历对象的所有属性，如果是对象或数组类型，则设置监听
    Object.keys(obj).forEach(key => {
      if (obj[key] !== null) {
        obj[key] = root.createProxy(obj[key])
      }
    });
    return proxy;
  }
  /**
   * 创建数组代理
   * @param {*} arr 
   * @returns 
   */
  createArrayProxy(arr) {
    let root = this;
    // 设置对象代理
    const proxy = new Proxy(arr, {
      set(target, property, value, receiver) {
        if (property === 'length' || root.isFilterMethodName(property) || !root.proxyJson) {
          return Reflect.set(...arguments);
        }
        let oldVal = target[property];
        let isSuccess = false;
        // 如果设置的属性是动态新添加的，则设置代理并赋值
        if (!target.hasOwnProperty(property) && receiver === proxy) {
          isSuccess = Reflect.set(...arguments);
          target[property] = root.createProxy(target[property])
          try {
            if (root.proxyJson.addCallback != null) {
              root.proxyJson.addCallback(target, property, value);
            }
          } catch (err) {
            console.error(err);
          }
        } else {  // 如果属性是更改的，则赋值
          isSuccess = Reflect.set(...arguments);
          // 如果将对象的值设置成了对象或数组，则重新设置代理
          target[property] = root.createProxy(target[property])
          try {
            if (root.proxyJson.changedCallback != null && property !== WATCH_FLAG) {
              root.proxyJson.changedCallback(target, property, oldVal, value);
            }
          } catch (err) {
            console.error(err);
          }
        }
        return isSuccess;
      }
    });
    for (let index in arr) {
      if (arr[index] != null) {
        arr[index] = this.createProxy(arr[index]);
      }
    }
    arr.watch = this;
    arr.__proto__ = newArrProto;
    return proxy;
  }
  /**
   * 是否是需要过滤的方法名
   * @param {*} name 
   * @returns 
   */
  isFilterMethodName (name) {
    return filterMethodNames.has(name);
  }
  /**
   * 删除指定JSON串中的属性
   * @param {*} json JSON对象
   * @param {*} propertyName 要删除的属性名
   * @returns 返回是否删除成功
   */
  static remove(json, path, propertyName) {
    let isSuccess = true;
    let val = path[propertyName];;
    if (OP.toString.call(path) === types.obj) {
      isSuccess = Reflect.deleteProperty(path, propertyName);
      if (isSuccess && path != null && propertyName !== WATCH_FLAG && WATCH_MAP.get(json) && WATCH_MAP.get(json).removeCallback) {
        try {
          json.removeCallback(path, propertyName, val);
        } catch (err) {
          console.error(err);
        }
      }
    } else if (OP.toString.call(path) === types.array) {
      if (path.length > propertyName) {
        path.splice(propertyName, 1);
      } else {
        isSuccess = false;
      }
    } else {
      isSuccess = false;
    }
    return isSuccess;
  }
  static version() {
    return '1.0.1';
  }
}